package cn.jh.common.exception;

import java.util.regex.Pattern;

import static com.google.common.base.MoreObjects.firstNonNull;

/**
 * @author Yichuan
 * @version 1.0
 */
public abstract class ErrorCodedException extends RuntimeException implements ErrorCoded {

    private static final long serialVersionUID = -7474346081707906996L;

    /**
     * Default error code number.
     */
    protected static final int DEFAULT_CODE = -1;
    /**
     * Default error message code.
     */
    protected static final String DEFAULT_ERROR_CODE = "errors.unknown_error";
    /**
     * Default error message.
     */
    protected static final String DEFAULT_ERROR_MESSAGE = "Unknown Error";

    /**
     * The error code pattern (i18n message code)
     * <pre>{@code
     * Pattern: [\w\.\-_]+(?:\.?[\w\.\-_])?
     * user.errors.notFound            match
     * user.errors－exists             not match
     * }</pre>
     */
    protected static final Pattern ERROR_CODE_PATTERN = Pattern.compile("^[\\w\\.\\-_]+$");

    /**
     * Resolve the given message. It will be returned if the given message match i18n message code pattern,
     * otherwise defaultMessage.
     *
     * @param message exception message (or i18n message code)
     * @param defaultMessage default exception message (or i18n message code)
     * @return Return i18n message code if the given message match i18n message code pattern.
     */
    protected static String resolveErrorCode(String message, String defaultMessage) {
        if (message == null || message.isEmpty()) {
            return defaultMessage;
        }
        if (message.startsWith("errors.") ||
                ERROR_CODE_PATTERN.matcher(message).matches()) {
            return message;
        }
        return defaultMessage;
    }

    /**
     * Error code number.
     */
    protected final int code;
    /**
     * Error message code. like {@code "object.somethingFailure"}
     */
    protected final String errorCode;

    /**
     * Constructs a new {@code ErrorCodedException} with given a error code number
     * and a {@link #DEFAULT_ERROR_CODE} error code and a {@link #DEFAULT_ERROR_MESSAGE} error message.
     *
     * @param code error code number.
     */
    public ErrorCodedException(int code) {
        this(code, DEFAULT_ERROR_CODE, DEFAULT_ERROR_MESSAGE);
    }

    public ErrorCodedException(String message) {
        this(DEFAULT_CODE, message);
    }

    public ErrorCodedException(int code, String errorCode) {
        this(code, resolveErrorCode(errorCode, DEFAULT_ERROR_CODE), firstNonNull(errorCode, DEFAULT_ERROR_MESSAGE));
    }

    public ErrorCodedException(int code, String errorCode, String errorMessage) {
        this(code, errorCode, errorMessage, null);
    }

    public ErrorCodedException(int code, String errorCode, String errorMessage, Throwable cause) {
        super(errorMessage, cause);
        this.code = code;
        this.errorCode = errorCode;
    }

    public ErrorCodedException(Throwable cause) {
        super(DEFAULT_ERROR_MESSAGE, cause);
        this.code = DEFAULT_CODE;
        this.errorCode = DEFAULT_ERROR_CODE;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getErrorCode() {
        return errorCode;
    }

    @Override
    public String getErrorMessage() {
        return getMessage();
    }
}
